package Contracts;

import Exceptions.InvariantException;
import Exceptions.PostCondException;
import Exceptions.PreCondException;
import Impl.Dir;
import Impl.Terrain;
import Impl.Type;
import Services.MoteurJeuService;
import Services.TerrainService;

public class MoteurJeuServiceContract extends MoteurJeuServiceDecorator{

	public MoteurJeuServiceContract(MoteurJeuService moteurJeuService) {
		super(moteurJeuService);
	}
	
	public void checkInvariants(){
		if(! ( getPasRestants()>=0))
			throw new InvariantException("inv 1 moteurJeu");
	}
	
	public boolean estFinie(){
		return super.estFinie();
	}
	public TerrainService getTerrain(){
		return super.getTerrain();
	}
	public boolean estGagne(){
		return super.estGagne();
	}
	public int getPasRestants(){
		return super.getPasRestants();
	}
	public boolean haveObjectMove() {
		return super.haveObjectMove();
	}
	public boolean haveHeroMove() {
		return super.haveHeroMove();
	}

	public void init(int i, TerrainService terrain){
		// Preconditions
		if(! (i>0))
			throw new PreCondException("pre 1 init");
		if(! terrain.isStarted())
			throw new PreCondException("pre 2 init");
		
		super.init(i, terrain);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(!(estFinie()==false))
			throw new PostCondException("post 1 init");
		if(!(estGagne()==false))
			throw new PostCondException("post 2 init");
		if(!(getPasRestants()==i))
			throw new PostCondException("post 3 init");
		if(!(getTerrain().equals(terrain)))
			throw new PostCondException("post 4 init");
		if(!(haveHeroMove()==false))
			throw new PostCondException("post 5 init");
		if(!(haveObjectMove()==false))
			throw new PostCondException("post 6 init");
	}	
	
	@Override
	public void verif(){
		// Preconditions
		if(!(!estFinie()))
			throw new PreCondException("pre 1 verif");
		if(!(haveHeroMove()))
			throw new PreCondException("pre 2 verif");
		if(!(haveObjectMove()))
			throw new PreCondException("pre 3 verif");
		/*if(!(getTerrain().isStarted()))
			throw new PreCondException("pre 4 verif");*/
		
		//AtPre
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getTerrain());
		boolean estFinieAtPre = estFinie();
		boolean estGagneAtPre = estGagne();
		int getPasRestantsAtPre = getPasRestants();
		int xJoueurAtPre = terrainAtPre.getXJoueur();
		int yJoueurAtPre = terrainAtPre.getYJoueur();
		Type type = terrainAtPre.getTypeBloc(xJoueurAtPre, yJoueurAtPre);
		
		// Invariants
		checkInvariants();
		
		super.verif();
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(!( getPasRestants()==getPasRestantsAtPre))
			throw new PostCondException("post 1 verif");
		
		if( terrainAtPre.getNbDiamant()==0 ){
			terrainAtPre.openDoor();
		}
		if(! getTerrain().equals(terrainAtPre))
			throw new PostCondException("post 2 verif");
			

		
		if(terrainAtPre.getOldTypeBloc(xJoueurAtPre, yJoueurAtPre)==Type.SortieOuverte){
			if(!( estFinie()==true && estGagne()==true ))
				throw new PostCondException("post 3 verif");
		}
		else if(type==Type.Diamant || type==Type.Rocher || getPasRestantsAtPre==0){
			if(!( estFinie()==true && estGagne()==false))
				throw new PostCondException("post 4 verif");
		}
		else if (!(estFinie()==estFinieAtPre && estGagne()==estGagneAtPre))
			throw new PostCondException("post 5 verif");
		
		if(!(haveHeroMove()==false))
			throw new PostCondException("post 6 verif");
		if(!(haveObjectMove()==false))
			throw new PostCondException("post 7 verif");
	}
	
	@Override
	public void moveObjects(){
		// Preconditions
		if(!(!haveObjectMove()))
			throw new PreCondException("pre 1 moveObject");
		if(!(!estFinie()))
			throw new PreCondException("pre 2 moveObject");
		if(!(haveHeroMove()))
			throw new PreCondException("pre 3 moveObject");
		/*if(!(getTerrain().isStarted()))
			throw new PreCondException("pre 4 moveObject");*/
		
		// AtPre
		boolean estFinieAtPre = estFinie();
		boolean estGagneAtPre = estGagne();
		boolean haveHeroMoveAtPre = haveHeroMove();
		int getPasRestantsAtPre = getPasRestants();
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getTerrain());
		
		// Invariants
		checkInvariants();
		
		super.moveObjects();
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (estFinie()==estFinieAtPre))
			throw new PostCondException("post 1 moveObject");
		if(! (estGagne()==estGagneAtPre))
			throw new PostCondException("post 2 moveObject");
		if(! (getPasRestants()==getPasRestantsAtPre))
			throw new PostCondException("post 3 moveObject");
		if(!(haveHeroMove()==haveHeroMoveAtPre))
			throw new PostCondException("post 4 moveObject");
		if(!(haveObjectMove()==true))
			throw new PostCondException("post 5 moveObject");
		
		for(int x=1; x<=terrainAtPre.getSizeX(); x++)
			for(int y=1; y<=terrainAtPre.getSizeY(); y++)
				if(terrainAtPre.getTypeBloc(x, y)==Type.Rocher || terrainAtPre.getTypeBloc(x, y)==Type.Diamant)
					if((!terrainAtPre.isChanged(x, y)) && (terrainAtPre.getTypeBloc(x, y+1)==Type.Vide||terrainAtPre.getTypeBloc(x, y+1)==Type.Hero))
						terrainAtPre.moveObject(x, y, x, y+1);
		terrainAtPre.validate();
		if(! (terrainAtPre.equals(getTerrain())))
			throw new PostCondException("post 6 moveObject");
	}
	
	@Override
	public void moveHero(Dir d){
		// Preconditions
		if(!(!haveHeroMove()))
			throw new PreCondException("pre 1 moveHero");
		if(!(!estFinie()))
			throw new PreCondException("pre 2 moveHero");
		if(!(!haveObjectMove()))
			throw new PreCondException("pre 3 moveHero");
		/*if(!(getTerrain().isStarted()))
			throw new PreCondException("pre 4 moveHero");*/
		
		// AtPre
		boolean estFinieAtPre = estFinie();
		boolean estGagneAtPre = estGagne();
		boolean haveObjectMoveAtPre = haveObjectMove();
		int getPasRestantsAtPre = getPasRestants();
		
		TerrainService terrainAtPre = new Terrain();
		terrainAtPre.clone(getTerrain());
		int xJoueurAtPre = terrainAtPre.getXJoueur();
		int yJoueurAtPre = terrainAtPre.getYJoueur();
		
		// Invariants
		checkInvariants();
		
		super.moveHero(d);
		
		// Invariants
		checkInvariants();
		
		// Postconditions
		if(! (estFinie()==estFinieAtPre))
			throw new PostCondException("post 1 moveObject");
		if(! (estGagne()==estGagneAtPre))
			throw new PostCondException("post 2 moveObject");
		
		if(! (getPasRestants()==getPasRestantsAtPre-1))
			throw new PostCondException("post 3 moveObject");

		
		if(!(haveHeroMove()==true))
			throw new PostCondException("post 4 moveHero");
		if(!(haveObjectMove()==haveObjectMoveAtPre))
			throw new PostCondException("post 5 moveHero");
		
		Type type;
		switch(d){
			case Bas:
				type = terrainAtPre.getTypeBloc(xJoueurAtPre, yJoueurAtPre+1);
				if(type==Type.Vide || type==Type.Terre || type==Type.SortieOuverte || type==Type.Diamant){
					terrainAtPre.putHero(xJoueurAtPre, yJoueurAtPre+1);
				}
				if(!(getTerrain().equals(terrainAtPre)))
					throw new PostCondException("post 6 moveHero");
				break;
			case Haut:
				type = terrainAtPre.getTypeBloc(xJoueurAtPre, yJoueurAtPre-1);
				if(type==Type.Vide || type==Type.Terre || type==Type.SortieOuverte){
					terrainAtPre.putHero(xJoueurAtPre, yJoueurAtPre-1);
				}
				if(!(getTerrain().equals(terrainAtPre)))
					throw new PostCondException("post 6 moveHero");
				break;
			case Droite: 
				type = terrainAtPre.getTypeBloc(xJoueurAtPre+1, yJoueurAtPre);
				if(type==Type.Vide || type==Type.Terre || type==Type.SortieOuverte || type==Type.Diamant)
					terrainAtPre.putHero(xJoueurAtPre+1, yJoueurAtPre);
				else if(type==Type.Rocher && terrainAtPre.getTypeBloc(xJoueurAtPre+2, yJoueurAtPre)==Type.Vide){
					terrainAtPre.moveObject(xJoueurAtPre+1, yJoueurAtPre, xJoueurAtPre+2, yJoueurAtPre);
					terrainAtPre.putHero(xJoueurAtPre+1, yJoueurAtPre);
				}
				if(!(getTerrain().equals(terrainAtPre)))
					throw new PostCondException("post 6 moveHero");
				break;
			case Gauche:
				type = terrainAtPre.getTypeBloc(xJoueurAtPre-1, yJoueurAtPre);
				if(type==Type.Vide || type==Type.Terre || type==Type.SortieOuverte || type==Type.Diamant){
					terrainAtPre.putHero(xJoueurAtPre-1, yJoueurAtPre);
				}
				else if(type==Type.Rocher && terrainAtPre.getTypeBloc(xJoueurAtPre-2, yJoueurAtPre)==Type.Vide){
					terrainAtPre.moveObject(xJoueurAtPre-1, yJoueurAtPre, xJoueurAtPre-2, yJoueurAtPre);
					terrainAtPre.putHero(xJoueurAtPre-1, yJoueurAtPre);
				}
				if(!(getTerrain().equals(terrainAtPre)))
					throw new PostCondException("post 6 moveHero");
				break;
			default: 
				if(!(getTerrain().equals(terrainAtPre)))
					throw new PostCondException("post 6 moveHero");
				break;
		}
	}

}
